package com.ftio.study.servlet;

import com.ftio.study.annotation.*;
import com.ftio.study.controller.MyControllers;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.List;

public class DispatcherServlet extends HttpServlet {

    List<String> classNames = new ArrayList<>();
    Map<String, Object> beans = new HashMap<>();
    Map<String, Object> handerMap = new HashMap<>();

    //<load-on-startup>0</load-on-startup>
    //tomcat 启动的时候要执行  创建 java  map... autowired
    public void init(ServletConfig config) {
//扫描 com.ftio.study.controller.MyController   Class<?>
        doScan("com.ftio");
//com.ftio.study.controller.MyController.class
        doInstance();

        doAutoWired();
        //路由
        urlMapping();
    }

    /**
     * url映射
     */
    private void urlMapping() {
        for (Map.Entry<String, Object> entry : beans.entrySet()) {
            //*******重点
            Object instance = entry.getValue();
            Class<?> clazz = instance.getClass();
            //是否为控制类
            if (clazz.isAnnotationPresent(MyController.class)) {
                MyRequestMapping requestMapping = clazz.getAnnotation(MyRequestMapping.class);
                String classPath = requestMapping.value();//   /my

                Method[] methods = clazz.getMethods();

                for (Method method : methods) {
                    if (method.isAnnotationPresent(MyRequestMapping.class)) {
                        MyRequestMapping requestMapping1 = method.getAnnotation(MyRequestMapping.class);
                        String methodPath = requestMapping1.value();  //   /query
                        handerMap.put(classPath + methodPath, method);
                    }
                }
            }
        }
    }

    /**
     * 自动注入
     */
    private void doAutoWired() {
        for (Map.Entry<String, Object> entry : beans.entrySet()) {
            //*******重点
            Object instance = entry.getValue();
            Class<?> clazz = instance.getClass();
            //是否为控制类
            if (clazz.isAnnotationPresent(MyController.class)) {
                Field[] fields = clazz.getDeclaredFields();//拿到控制类里面的所有字段属性
                for (Field field : fields) {
                    if (field.isAnnotationPresent(MyAutowired.class)) {
                        MyAutowired autowired = field.getAnnotation(MyAutowired.class);
                        String key = autowired.value();

                        Object value = beans.get(key);
                        field.setAccessible(true);//访问权限打开
                        try {
                            field.set(instance, value);//注入
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    /**
     * 实例化扫描出来的所有类
     */
    private void doInstance() {
        for (String className : classNames) {
            //com.ftio.study.controller.MyController.class  只需要到类名
            String cn = className.replace(".class", "");//com.ftio.study.controller.MyController
            try {
                //*************重点
                Class<?> clazz = Class.forName(cn);
                //是否为控制类
                if (clazz.isAnnotationPresent(MyController.class)) {
                    Object value = clazz.newInstance();
                    //map.put(key,value)
                    MyRequestMapping reqMap = clazz.getAnnotation(MyRequestMapping.class);
                    String key = reqMap.value();
                    beans.put(key, value);
                }
                //是否为service类
                else if (clazz.isAnnotationPresent(MyService.class)) {
                    Object value = clazz.newInstance();
                    //map.put(key,value)
                    MyService reqMap = clazz.getAnnotation(MyService.class);
                    String key = reqMap.value();
                    beans.put(key, value);//放到IOC容器里面
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 扫描
     *
     * @param baePackage mvc.xml---->basePackage=""
     */
    private void doScan(String baePackage) {
        //basepackage=com.ftio
        //扫描编译好的所有类路径  /com/ftio
        URL url = null;
        url = this.getClass().getClassLoader().getResource("/" + baePackage.replaceAll("\\.", "/"));
        String fileStr = url.getFile();
        File file = new File(fileStr);
        String[] filesStr = file.list();//com.ftio下面的所有文件夹或者.class文件
        for (String path : filesStr) {
            File filePath = new File(fileStr + "/" + path);//com/ftio/study
            if (filePath.isDirectory()) {
                doScan(baePackage + "." + path);
            } else {
                //com.....OrderService.class
                classNames.add(baePackage + "." + filePath.getName());
            }
        }
    }


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求路径   /my/query  ----> method
        String uri = req.getRequestURI();//
        String context = req.getContextPath();//获取项目名称

        String path = uri.replace(context, "");  //  /my/querey   key

        Method method = (Method) handerMap.get(path);

        MyControllers instance = (MyControllers) beans.get("/" + path.split("/")[1]);  //   /my

        Object args = hand(req, resp, method);

        try {
            method.invoke(instance, args);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }

    //策略模式？
    private static Object[] hand(HttpServletRequest req, HttpServletResponse resp, Method method) {
        //拿到当前待执行的方法有哪些参数
        Class<?>[] paramClazzs = method.getExceptionTypes();
        //根据参数的个数，new 一个参数的数组，将方法里的所有参数赋值到args来
        Object[] args = new Object[paramClazzs.length];
        int args_i = 0;
        int index = 0;
        for (Class<?> paramClazz : paramClazzs) {
            if (ServletRequest.class.isAssignableFrom(paramClazz)) {
                args[args_i++] = req;
            }
            if (ServletResponse.class.isAssignableFrom(paramClazz)) {
                args[args_i++] = resp;
            }
            //从0-3判断有没有RequestParam注解，很明显paramClazz为0和1时，不是
            //当为2和3时为RequestParam，需要解析
            //value=name
            Annotation[] paramAns = method.getParameterAnnotations()[index];
            if (paramAns.length > 0) {
                for (Annotation paramAn : paramAns) {
                    if (MyRequestParam.class.isAssignableFrom(paramAn.getClass())) {
                        MyRequestParam rp = (MyRequestParam) paramAn;
                        args[args_i++] = req.getParameter(rp.value());
                    }
                }
            }
            index++;
        }
        return args;
    }


}
